Õppige TypeScripti deklaratsioonifaile (.d.ts), et avada tüübiohutus ja automaatlõpetus JavaScripti teekidele. Kasutage @types'i ja looge oma definitsioone.
JavaScripti ökosüsteemi avamine: sügav sukeldumine TypeScripti deklaratsioonifailidesse
TypeScript on muutnud kaasaegset veebiarendust, tuues staatilise tüüpimise JavaScripti dünaamilisse maailma. See tüübiohutus pakub uskumatuid eeliseid: vigade püüdmist kompileerimise ajal, võimsat redaktori automaatlõpetust ja suurte koodibaaside olulist hooldatavuse paranemist. Kuid suur väljakutse tekib siis, kui tahame kasutada olemasolevate JavaScripti teekide tohutut ökosüsteemi – millest enamikku ei kirjutatud TypeScriptis. Kuidas meie rangelt tüübitud TypeScripti kood mõistab tüüpideta JavaScripti teegi kujundeid, funktsioone ja muutujaid?
Vastus peitub TypeScripti deklaratsioonifailides. Need failid, mis on tuvastatavad nende .d.ts laiendi järgi, on oluline sild TypeScripti ja JavaScripti maailmade vahel. Need toimivad kavandina või API-lepinguna, kirjeldades kolmanda osapoole teegi tüüpe, ilma et sisaldaksid selle tegelikku implementatsiooni. Selles põhjalikus juhendis uurime kõike, mida peate teadma, et hallata enesekindlalt mis tahes JavaScripti teegi tüübidefinitsioone oma TypeScripti projektides.
Mis täpselt on TypeScripti deklaratsioonifailid?
Kujutage ette, et olete palganud töövõtja, kes räägib ainult teist keelt. Nendega tõhusaks töötamiseks vajaksite tõlkijat või üksikasjalikke juhiseid keeles, mida te mõlemad mõistate. Deklaratsioonifail teenib seda täpset eesmärki TypeScripti kompilaatori (töövõtja) jaoks.
A .d.ts fail sisaldab ainult tüübiteavet. See hõlmab:
- Funktsioonide ja meetodite signatuurid (parameetrite tüübid, tagastustüübid).
- Muutujate ja nende tüüpide definitsioonid.
- Liideste ja tüübialiaste definitsioonid keerukate objektide jaoks.
- Klasside definitsioonid, sealhulgas nende omadused ja meetodid.
- Nimeruumi ja mooduli struktuurid.
Oluline on, et need failid ei sisalda täidetavat koodi. Need on puhtalt staatiliseks analüüsiks. Kui impordite JavaScripti teegi, näiteks Lodash, oma TypeScripti projekti, otsib kompilaator vastavat deklaratsioonifaili. Kui see selle leiab, saab see teie koodi valideerida, pakkuda intelligentset automaatlõpetust ja tagada, et kasutate teeki õigesti. Kui see seda ei tee, kuvatakse viga, näiteks: Could not find a declaration file for module 'lodash'.
Miks deklaratsioonifailid on professionaalses arenduses vältimatud
JavaScripti teekide kasutamine ilma õigete tüübidefinitsioonideta TypeScripti projektis õõnestab TypeScripti kasutamise esialgset põhjust. Vaatleme lihtsat stsenaariumi, kasutades populaarset utiliitide teeki Lodash.
Maailm ilma tüübidefinitsioonideta
Ilma deklaratsioonifailita pole TypeScriptil aimugi, mis lodash on või mida see sisaldab. Koodi kompileerimiseks võite kiusatusse sattuda kasutama kiirparandust, näiteks:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Autocomplete? No help here.
// Type checking? No. Is 'username' the correct property?
// The compiler allows this, but it might fail at runtime.
_.find(users, { username: 'fred' });
Sel juhul on muutuja _ tüübiks any. See ütleb TypeScriptile efektiivselt: "Ära kontrolli midagi, mis on selle muutujaga seotud." Kaotate kõik eelised: puudub automaatlõpetus, puudub argumentide tüübikontroll ja puudub kindlus tagastustüübi osas. See on viljakas pinnas käitusvigade tekkimiseks.
Maailm tüübidefinitsioonidega
Nüüd vaatame, mis juhtub, kui pakume vajaliku deklaratsioonifaili. Pärast tüüpide installimist (mida käsitleme järgmises osas) muutub kogemus oluliselt:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Redaktor pakub automaatlõpetust 'find' ja teiste lodashi funktsioonide jaoks.
// 2. Kursori viimine 'find' peale näitab selle täielikku signatuuri ja dokumentatsiooni.
// 3. TypeScript näeb, et `users` on `User` objektide massiiv.
// 4. TypeScript teab, et `find` predikaat `User[]` puhul peaks hõlmama `user` või `active`.
// ÕIGE: TypeScript on rahul.
const fred = _.find(users, { user: 'fred' });
// VIGA: TypeScript püüab vea kinni!
// Property 'username' does not exist on type 'User'.
const betty = _.find(users, { username: 'betty' });
Erinevus on öö ja päev. Me saame täieliku tüübiohutuse, parema arendajakogemuse tööriistade kaudu ja dramaatiliselt vähenevad potentsiaalsed vead. See on professionaalne standard TypeScriptiga töötamisel.
Tüübi definitsioonide leidmise hierarhia
Kuidas siis hankida need maagilised .d.ts failid oma lemmikteekide jaoks? On olemas väljakujunenud protsess, mis katab enamiku stsenaariumidest.
1. samm: Kontrollige, kas teek komplekteerib oma tüübid
Parim stsenaarium on see, kui teek on kirjutatud TypeScriptis või selle haldajad pakuvad ametlikke deklaratsioonifaile samas paketis. See muutub üha tavalisemaks kaasaegsete, hästi hooldatud projektide puhul.
Kuidas kontrollida:
- Installige teek tavapäraselt:
npm install axios - Vaadake teegi kausta
node_modules/axiossisse. Kas näete seal.d.tsfaile? - Kontrollige teegi
package.jsonfailist välja"types"või"typings". See väli osutab otse peamisele deklaratsioonifailile. Näiteks Axios'epackage.jsonsisaldab:"types": "index.d.ts".
Kui need tingimused on täidetud, oletegi valmis! TypeScript leiab ja kasutab neid komplekteeritud tüüpe automaatselt. Edasisi toiminguid pole vaja.
2. samm: DefinitelyTyped projekt (@types)
Tuhandete JavaScripti teekide jaoks, mis oma tüüpe ei komplekteeri, on globaalne TypeScripti kogukond loonud uskumatu ressursi: DefinitelyTyped.
DefinitelyTyped on tsentraliseeritud, kogukonna hallatav GitHubi hoidla, mis majutab kvaliteetseid deklaratsioonifaile tohutule hulgale JavaScripti pakettidele. Need definitsioonid avaldatakse npm registris @types nimeruumi all.
Kuidas seda kasutada:
Kui teek, nagu lodash, ei komplekteeri oma tüüpe, installite lihtsalt sellele vastava @types paketi arendussõltuvusena:
npm install --save-dev @types/lodash
Nimetamiskonventsioon on lihtne ja ennustatav: paketi nimega package-name korral on selle tüübid peaaegu alati aadressil @types/package-name. Saate otsida saadaolevaid tüüpe npm veebisaidilt või otse DefinitelyTyped hoidlast.
Miks --save-dev? Deklaratsioonifaile on vaja ainult arenduse ja kompileerimise ajal. Need ei sisalda käitusaegset koodi, seega ei tohiks neid lisada teie lõplikku tootmispaketti. Nende installimine devDependency-na tagab selle eraldamise.
3. samm: Kui tüüpe pole olemas – looge oma
Mis siis, kui kasutate vanemat, nišš- või sisemist privaatset teeki, mis ei komplekteeri tüüpe ega ole DefinitelyTypedis? Sel juhul peate käärima varrukad üles ja looma oma deklaratsioonifaili. Kuigi see võib kõlada hirmutavalt, saate alustada lihtsalt ja lisada detaile vastavalt vajadusele.
Kiirparandus: Lühike globaalse mooduli deklaratsioon
Mõnikord on teil lihtsalt vaja, et projekt kompileeruks ilma vigadeta, samal ajal kui mõtlete välja õige tüüpimisstrateegia. Saate luua oma projekti faili (nt declarations.d.ts või types/global.d.ts) ja lisada lühikese deklaratsiooni:
// in a .d.ts file
declare module 'some-untyped-library';
See ütleb TypeScriptile: "Usalda mind, moodul nimega 'some-untyped-library' on olemas. Käsitle kõike, mis sellest imporditakse, tüübina any." See vaigistab kompilaatori vea, kuid nagu oleme arutanud, ohverdab see selle teegi kogu tüübiohutuse. See on ajutine plaaster, mitte pikaajaline lahendus.
Põhjaliku kohandatud deklaratsioonifaili loomine
Parem lähenemine on hakata defineerima teegi osade tüüpe, mida te tegelikult kasutate. Oletame, et meil on lihtne teek nimega `string-utils` mis ekspordib ühte funktsiooni.
// In node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
Saame luua string-utils.d.ts faili spetsiaalsesse `types` kataloogi oma projekti juurkaustas.
// In my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// You could add other function definitions here as you use them
// export function slugify(str: string): string;
}
Nüüd peame TypeScriptile ütlema, kust leida meie kohandatud tüübidefinitsioonid. Teeme seda tsconfig.json failis:
{
"compilerOptions": {
// ... other options
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
Selle seadistusega, kui impordite import { capitalize } from 'string-utils', leiab TypeScript teie kohandatud deklaratsioonifaili ja pakub määratud tüübiohutuse. Saate seda faili järk-järgult täiendada, kui kasutate teegi rohkem funktsioone.
Süvenemine: deklaratsioonifailide autorlus
Uurime mõningaid arenenumaid kontseptsioone, millega kohtute deklaratsioonifailide kirjutamisel või lugemisel.
Erinevate eksportide deklareerimine
JavaScripti moodulid saavad asju eksportida mitmel viisil. Teie deklaratsioonifail peab vastama teegi ekspordistruktuurile.
- Nimega ekspordid: See on kõige tavalisem. Nägime seda eespool `export function capitalize(...)` näitega. Saate eksportida ka konstante, liideseid ja klasse.
- Vaikimisi eksport: Teekide jaoks, mis kasutavad `export default`.
- UMD globaalid: Vanemate teekide puhul, mis on loodud brauserites töötama
<script>sildi kaudu, kinnituvad nad sageli globaalse `window` objektile. Saate need globaalsed muutujad deklareerida. - `export =` ja `import = require()`: See süntaks on mõeldud vanemate CommonJS moodulite jaoks, mis kasutavad `module.exports = ...`. Näiteks, kui teek teeb `module.exports = myClass;`.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise<void>;
}
declare module 'my-default-lib' {
// For a function default export
export default function myCoolFunction(): void;
// For an object default export
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Declares a global variable '$' of a certain type
declare var $: JQueryStatic;
// in my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// in your app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
Kuigi see on kaasaegsete ES moodulite puhul harvem, on see kriitilise tähtsusega paljude vanemate, kuid endiselt laialdaselt kasutatavate Node.js pakettide ühilduvuse tagamiseks.
Mooduli laiendamine: olemasolevate tüüpide laiendamine
Üks võimsamaid funktsioone on mooduli laiendamine (tuntud ka kui deklaratsioonide ühendamine). See võimaldab teil lisada omadusi olemasolevatele liidestele, mis on defineeritud teise paketi deklaratsioonifailis. See on äärmiselt kasulik teekide jaoks, millel on pistikprogrammi arhitektuur, nagu Express või Fastify.
Kujutage ette, et kasutate Expressis vahevara, mis lisab `user` omaduse `Request` objektile. Ilma laiendamiseta kurdaks TypeScript, et `user` ei eksisteeri `Request` objektil.
Siin on, kuidas saate TypeScriptile sellest uuest omadusest teada anda:
// in your types/express.d.ts file
// We must import the original type to augment it
import { UserProfile } from './auth'; // Assuming you have a UserProfile type
// Tell TypeScript we're augmenting the 'express-serve-static-core' module
declare module 'express-serve-static-core' {
// Target the 'Request' interface inside that module
interface Request {
// Add our custom property
user?: UserProfile;
}
}
Nüüd, kogu teie rakenduses, on Expressi `Request` objekt korrektselt tüübitud valikulise `user` omadusega ja saate täieliku tüübiohutuse ja automaatlõpetuse.
Kolmekordse kaldkriipsuga direktiivid
Võite mõnikord näha .d.ts failide ülaosas kommentaare, mis algavad kolme kaldkriipsuga (///). Need on kolmekordse kaldkriipsuga direktiivid, mis toimivad kompilaatori juhistena.
/// <reference types="..." />: See on kõige levinum. See lisab sõnaselgelt teise paketi tüübidefinitsioonid sõltuvusena. Näiteks WebdriverIO pistikprogrammi tüübid võivad sisaldada/// <reference types="webdriverio" />, kuna selle enda tüübid sõltuvad WebdriverIO põhitüüpidest./// <reference path="..." />: Seda kasutatakse sõltuvuse deklareerimiseks sama projekti teisele failile. See on vanem süntaks, mis on suuresti asendatud ES mooduli importidega.
Deklaratsioonifailide haldamise parimad tavad
- Eelistage komplekteeritud tüüpe: Teekide valikul eelistage neid, mis on kirjutatud TypeScriptis või komplekteerivad oma ametlikud tüübidefinitsioonid. See annab märku pühendumisest TypeScripti ökosüsteemile.
- Hoidke
@typesdevDependenciesall: Installige@typespaketid alati--save-devvõi-Dabil. Neid ei ole vaja teie tootmiskoodi jaoks. - Sünkroniseerige versioonid: Levinud vigade allikas on teegi versiooni ja selle
@typesversiooni mittevastavus. Teegi suurem versiooniuuendus (nt v2-lt v3-le) toob tõenäoliselt kaasa API murdvaid muutusi, mis peavad kajastuma@typespaketi versioonis. Proovige neid sünkroonis hoida. - Kasutage
tsconfig.jsonkontrolliks: Teietsconfig.jsonfailis olevad kompilaatori valikudtypeRootsjatypesannavad teile peene kontrolli selle üle, kust TypeScript deklaratsioonifaile otsib.typeRootsütleb kompilaatorile, milliseid kaustu kontrollida (vaikimisi on see./node_modules/@types), jatypesvõimaldab teil sõnaselgelt loetleda, milliseid tüübipakette kaasata. - Panustage tagasi: Kui kirjutate põhjaliku deklaratsioonifaili teegi jaoks, millel seda pole, kaaluge selle panustamist DefinitelyTyped projekti. See on suurepärane viis anda tagasi globaalsele arendajate kogukonnale ja aidata tuhandeid teisi.
Järeldus: tüübiohutuse laulmata kangelased
TypeScripti deklaratsioonifailid on laulmata kangelased, mis võimaldavad sujuvalt integreerida JavaScripti dünaamilise, laialivalguva maailma robustsesse, tüübiohutusse arenduskeskkonda. Need on kriitiline lüli, mis annab meie tööriistadele jõu, hoiab ära lugematuid vigu ja muudab meie koodibaasid vastupidavamaks ja isedokumenteerivamaks.
Mõistes, kuidas leida, kasutada ja isegi luua oma .d.ts faile, te mitte ainult ei paranda kompilaatori viga – te tõstate kogu oma arendustöövoogu. Te avate nii TypeScripti kui ka JavaScripti teekide rikkaliku ökosüsteemi täieliku potentsiaali, luues võimsa sünergia, mis toob kaasa parema ja usaldusväärsema tarkvara globaalsele publikule.